重新编译 mice

2021-11-01 20:00:00

今天聊聊 MATLAB。咦,MATLAB 一直不都是调侃的对象吗,怎么成主角了?No No No,所谓“圣人无常师”,只有小学生才会争论语言的高低贵贱,成年人都是驾驭自如为我所用的。

本教程只针对 Microsoft Windows 10 操作系统!

什么是mice

mice 是 MATLAB 版本的 SPICE Toolkit。SPICE Toolkit 享有盛名,这里不再赘述,想要了解更多,请移步其官方网站

为什么要重新编译 mice

我曾经写过一个软件,其中一些核心功能依赖于 mice,但是目前 mice(N0066 版本,released April 10, 2017)尚未完全把 SPICE Toolkit 的所有功能适配到 MATLAB。比如没有提供cspice_spkw09函数,但是这个函数却非常有用。

显然不能坐等官方团队在下一个版本进行适配,应该主动对 mice 进行定制,按照自己的需求增加功能,最后重新编译 mice。只有这样,以后才能更好地、可持续地利用 mice。

下载 mice

在官网下载mice.zip(需要科学上网),解压。文件结构为:

(installation directory)
         |
         mice(下文中记作<mice>)
            |
            data
            ...
            include
            lib
            src
               |
               brief_c
               ...
               spacit_c
               cspice
               csupport
               mice
               micecook

实现cspice_spkw09函数

cspice_spkw09函数为例,介绍如何实现自己的需求。把以下代码写入<mice>\src\mice\mice.c的适当位置:

/*
   void spkw09_c ( SpiceInt             handle,
                   SpiceInt             body,
                   SpiceInt             center, 
                   ConstSpiceChar     * frame,
                   SpiceDouble          first,
                   SpiceDouble          last,
                   ConstSpiceChar     * segid,
                   SpiceInt             degree,
                   SpiceInt             n,
                   ConstSpiceDouble     states[][6],
                   ConstSpiceDouble     epochs[]     )
*/
void cspice_spkw09(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{

   SpiceInt             handle;
   SpiceInt             body;
   SpiceInt             center;
   SpiceChar            frame   [DEFAULT_STR_LENGTH+1];
   SpiceDouble          first;
   SpiceDouble          last;
   SpiceChar            segid   [DEFAULT_STR_LENGTH+1];
   SpiceInt             degree;
   SpiceInt             n;
   ConstSpiceDouble   * states;
   ConstSpiceDouble   * epochs;

   struct extra_dims  * extra;
   struct argcheck ArgCheck[] =
      {
      { "handle",  MiceInt,    0, { 0 },    0},
      { "body",    MiceInt,    0, { 0 },    0},
      { "center",  MiceInt,    0, { 0 },    0},
      { "frame",   MiceChar,   0, { 0 },    0},
      { "first",   MiceDouble, 0, { 0 },    0},
      { "last",    MiceDouble, 0, { 0 },    0},
      { "segid",   MiceChar,   0, { 0 },    0},
      { "degree",  MiceInt,    0, { 0 },    0},
      { "states",  MiceDouble, 2, { 6, 0 }, 0},
      { "epochs",  MiceDouble, 2, { 1, 0 }, 0},
      };

   check_arg_num( nrhs, nlhs, 10, 0);

   extra = mice_checkargs(nlhs,plhs,nrhs,prhs,ArgCheck);

   /*
   Number of state sets = number of elements in array divided by six;
   we expect a 6xN array.
   */
   n = mxGetNumberOfElements( prhs[9]) / 6;

   handle  = S_INT_ARGV(1);
   body    = S_INT_ARGV(2);
   center  = S_INT_ARGV(3);

   mxGetString(prhs[4], frame, DEFAULT_STR_LENGTH);

   first   = S_DBL_ARGV(5);
   last    = S_DBL_ARGV(6);

   mxGetString(prhs[7], segid, DEFAULT_STR_LENGTH);

   degree  = S_INT_ARGV(8);

   states  = (ConstSpiceDouble*)mxGetData(prhs[9]);
   epochs  = (ConstSpiceDouble*)mxGetData(prhs[10]);

   spkw09_c ( handle,
              body,
              center,
              frame,
              first,
              last,
              segid,
              degree,
              n,
              (Nx6d)states,
              (ConstSpiceDouble(*)[1]) epochs );

   CHECK_CALL_FAILURE( SCALAR );

}

编译mice.mexw64的步骤

正式编译之前,请注意:

cspice源代码没有使用mwSize,而是使用int,因此鲁棒性很差,编译时必须采用-compatibleArrayDims模式

SPICE Toolkit version is N0066。测试环境为 MATLAB 2020b 和 Microsoft Visual Studio 14.0


  1. <mice>\src\mice\mice.c靠近头部的位置,添加以下代码:
#if _MSC_VER>=1900  
_ACRTIMP_ALT FILE* __cdecl __acrt_iob_func(unsigned);
#ifdef __cplusplus   
extern "C"
#endif   
FILE* __cdecl __iob_func(unsigned i) {
    return __acrt_iob_func(i);
}
#endif

为什么要加这行代码?解释:

因为不同版本的 VS 对stdin, stdout, stderr的定义不同(参见C:\Program Files (x86)\Windows Kits\10\Include\10.0.10150.0\ucrt\corecrt_wstdio.h文件)

在此之后,有两种方案可供选择


第一种方案,使用官方提供的cspice.lib库文件,位于<mice>\lib\路径下(省时,推荐

  1. 在MATLAB,把以下代码保存成MakeProductWithCSPICELIB.m脚本,并运行
function MakeProductWithCSPICELIB()

% mice文件夹的路径(根据具体情况更改)
MICE_PATH = fileparts( mfilename('fullpath') );

% 64位libcmt.lib库的路径(根据具体情况更改)
LIBCMT_PATH = 'C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\lib\amd64\';

% 跳转到
cd( MICE_PATH );

% 正式编译
mex('-compatibleArrayDims',...
    '-output' , '..\..\lib\mice',...   % 定义输出的路径和文件名
    '-I..\..\include',...              % 头文件的路径中
    'mice.c',...
    'zzmice.c',...
    'zzmice_CreateIntScalar.c',...
    '..\..\lib\cspice.lib',...         % lib
    [LIBCMT_PATH,'libcmt.lib'] );      % lib

end

注意事项如下,请务必认真阅读:

  1. 测试环境为 MATLAB 2020b 和 Microsoft Visual Studio 14.0
  2. 脚本应放置在<mice>\src\mice\路径中,即与mice.c同一路径
  3. 编译时引用了<mice>\include\路径中的头文件,请勿随意修改该文件夹下的文件
  4. 编译时需要cspice.lib库,请确保该文件存在于<mice>\lib\路径中
  5. 编译时需要libcmt.lib库,该库文件由 VS 提供,请确认该库文件的具体路径,并对脚本中的 LIBCMT_PATH 变量及时做出修正
  6. 编译后生成的文件为mice.mexw64,位于<mice>\lib\路径中,如果编译前该路径中已经存在mice.mexw64文件,会不提醒地将其覆盖,所以最好备份

第二种方案,不使用官方提供的cspice.lib,自己编译源代码(不省时,易出错,不推荐

  1. 编译vcf2c.lib,具体步骤请参阅后文。编译后,把库文件放置在<mice>\lib\路径中。因为 cspice 源代码广泛使用 f2c 工具,因此依赖该库
  2. <mice>\include\缺少unistd.h头文件,请补充,其源代码如下:
/* This file is part of the Mingw32 package.
    unistd.h maps (roughly) to io.h
*/

#ifndef _UNISTD_H
#define _UNISTD_H

#include <io.h>

#endif
  1. 下载list.txt文件,链接https://gitee.com/openmdt/mice-compile/raw/master/list.txt,放到<mice>\src\mice\路径下。该文件列举了编译所需的.c源文件(是我一个一个试出来的,缺一不可
  2. 在 MATLAB,把以下代码保存成MakeProductWithoutCSPICELIB.m脚本,并运行
function MakeProductWithoutCSPICELIB()

% mice文件夹的路径(根据具体情况更改)
MICE_PATH = fileparts( mfilename('fullpath') );

% 跳转到
cd( MICE_PATH );

% 打开list.txt
fid = fopen('list.txt','r');

% 找到编译所需的.c源文件
p = '..\cspice\';
k = 1;
while ~feof(fid)
    temp = fgetl(fid);
    list{k} = [p , temp];
    k = k + 1;
end
fclose(fid);

% 正式编译
mex('-compatibleArrayDims',...
    '-output' , '..\..\lib\mice',...   % 定义输出的路径和文件名
    '-I..\..\include',...              % 头文件的路径中
    '..\..\lib\vcf2c.lib',...
    'C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\lib\amd64\libcmt.lib',...
    'mice.c',...
    'zzmice.c',...
    'zzmice_CreateIntScalar.c',...
    list{:} );

end

注意事项如下,请务必认真阅读:

  1. 测试环境为 MATLAB 2020b 和 Microsoft Visual Studio 14.0
  2. 脚本应放置在<mice>\src\mice\路径中,即与mice.c同一路径
  3. 编译时引用了<mice>\include\路径中的头文件,请勿随意修改该文件夹下的文件
  4. 编译时需要libcmt.lib库,该库文件由 VS 提供,请确认该库文件的具体路径,并对脚本中的 LIBCMT_PATH 变量及时做出修正
  5. 编译时需要vcf2c.lib库,请确保已经按照相关步骤编译出该文件
  6. 编译后生成的文件为mice.mexw64,位于<mice>\lib\路径中,如果编译前该路径中已经存在mice.mexw64文件,会不提醒地将其覆盖,所以最好备份

最后,即使按照以上步骤做了,也不一定能成功,比较依赖编译环境。但是,在 MATLAB 2020b 和 Microsoft Visual Studio 14.0 环境下,经测试,没有问题。如果没成功,请不要迁怒于任何一个工具,它们都是相当优秀的。

编译vcf2c.lib的步骤

备注:

测试环境为 Microsoft Visual Studio 14.0。下文中的路径根据 VS 的具体安装位置灵活调整

具体步骤:

  1. www.netlib.org/f2c/ 下载libf2c.zip压缩包,并解压到<path>\libf2c\路径
  2. 在“此电脑”右击,选择“属性”,选择“高级系统设置”,点击“环境变量”
  3. 在“系统变量”面板上,点击“新建”,“变量名”填写INCLUDE,“变量值”填写C:\Program Files (x86)\Windows Kits\10\Include\10.0.10150.0\ucrt;C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include
  4. 在“系统变量”面板上,点击“新建”,“变量名”填写LIB,“变量值”填写C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\lib\amd64;C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Lib\x64;C:\Program Files (x86)\Windows Kits\10\Lib\10.0.10150.0\ucrt\x64
  5. 在“系统变量”面板上,点击“新建”,“变量名”填写Path,“变量值”填写C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64
  6. 可以在C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64路径下找到 64 位nmake.exe
  7. 运行 cmd.exe,输入要到达的盘,比如D:,再输入CD <path>\libf2c\
  8. 接着输入nmake -f makefile.vc
  9. 完成
  10. 如果计算机装有 Mathematica,不妨去<Mathematica安装路径>\SystemFiles\Components\WSMCore\lib\win64\VS2013路径下,看看能否找到f2c.lib,这就是我们想要的库文件,Mathematica 已经帮我们编译好了,亲测可用

Author

青崖同学

Release

2021-11-01 20:00:00

License

Creative Commons